<style scoped>
  .layout {
    border: 0px solid #d7dde4;
    background: #f5f7f9;
    position: relative;
    border-radius: 0px;
    overflow: hidden;
    height: 100%;
  }
</style>
<style>
  .ivu-switch, .ivu-switch:after,
  .ivu-message-notice-content,
  .ivu-radio-group-button .ivu-radio-wrapper:first-child,
  .ivu-radio-group-button .ivu-radio-wrapper:last-child,
  .ivu-alert,
  .ivu-btn,
  .ivu-btn-small,
  .ivu-input ,
  .ivu-card,
  .ivu-input-group-append,
  .ivu-input-group-prepend,
  .ivu-modal-content,
  .ivu-tabs.ivu-tabs-card>.ivu-tabs-bar .ivu-tabs-tab {
    border-radius: 0;
  }

  .ivu-layout {
    height: 100%;
  }
  .header {
    display: none;
  }
</style>
<template>
  <div class="layout">
    <Layout>
      <Header style="padding: 0 10px;">
        <Button @click="showLoginModal()" size="small" icon="ios-download-outline"  type="primary">连接服务器</Button>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <Button size="small" v-if="currentConnectionId != ''" icon="ios-swap" type="success" @click="openPubSubTab()">发布订阅</Button>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <Button size="small" v-if="currentConnectionId != ''" icon="md-laptop" type="warning" @click="showJsonModal = true">CLI</Button>
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        <Button size="small" v-if="currentConnectionId != ''" icon="md-alert" type="info" @click="openInfoTab()">服务信息</Button>
      </Header>

      <Layout :style="{height: '100%'}">
        <Sider hide-trigger :style="{background: '#fff', width:'250px',maxWidth:'250px', minWidth:'250px' , 'overflow-y': 'auto', 'overflow-x': 'hidden'}">
          <Tree :data="connectionTreeList" :load-data="loadData" empty-text="" @on-select-change="selectChange"></Tree>
        </Sider>
        <Layout>
          <Content :style="{ height: '100%', background: '#fff', borderLeft: '1px solid #ccc'}">
            <div v-if="currentConnectionId && currentDbIndex > -1 && typeof tabs[getTabsKey()] !== 'undefined' && !isEmptyObj(tabs[getTabsKey()]['keys'])" :style="{height: '100%' }">
            <Tabs @on-tab-remove="handleTabRemove" type="card" :value="currentKey" :animated="false" :style="{ background: '#fff', height: '100%' }">
              <TabPane v-for="(data, key) in tabs[getTabsKey()]['keys']" closable :name="key" :key="key" :label="currentConnection + '::DB' + currentDbIndex + '::' + key" >
                <Row type="flex">
                  <Col span="12">
                    <Input v-model="key" readonly>
                      <span slot="prepend">{{data.type.toUpperCase() }}:</span>
                      <span slot="append">TTL: {{data.ttl}}</span>
                    </Input>
                  </Col>
                  <Col span="12" style="text-align: right;">
                    <ButtonGroup>
                      <Button :loading="buttonLoading" @click="removeKey(key)">删除</Button>
                      <Button :loading="buttonLoading" @click="flushKey(key)">刷新</Button>
                      <Button :loading="buttonLoading" @click="setTTL(key, data)">重置TTL</Button>
                    </ButtonGroup>
                  </Col>
                </Row>
                <div v-if="data.type === 'string'" style="margin-top: 4px;">
                  <Input v-model="data.data" v-if="!textType" type="textarea" :autosize="{minRows: 20,maxRows: 30}" placeholder="Enter something..."></Input>
                  <vue-json-pretty
                    v-if="textType"
                    :path="'res'"
                    :data="formatJson(data.data, key)"
                  >
                  </vue-json-pretty>

                  <Row type="flex">
                    <Col span="24" style="text-align: right">
                      <i-switch size="large" v-model="textType" style="height: 24px; line-height: 23px;margin-right: 5px;">
                        <span slot="open" >Json</span>
                        <span slot="close">Text</span>
                      </i-switch>
                      <Button style="float: right" size="small" type="info" @click="updateValue(key, data, 'value')" :loading="buttonLoading">保存</Button>
                    </Col>
                  </Row>
                </div>
                <div v-else style="margin-top: 4px;">
                  <Row type="flex">
                    <Col span="16">
                      <Table highlight-row @on-row-click="getRowData" ref="currentRowTable" border height="400" :columns="getColumns(data.type)" :data="formatItem(data.type,data.data)"></Table>
                    </Col>
                    <Col span="8">
                      <Card style="height:400px;border-left: none;" dis-hover>
                        <p slot="title">
                          操作
                        </p>
                        <Button long @click="addRow(key, data)">插入行</Button>
                        <br/>
                        <br/>
                        <Button long @click="removeRow(key,data)">删除行</Button>
                        <br/><br/><br/><br/><br/><br/><br/><br/><br/>
                        <Input placeholder="列表中查询..." v-model="searchKey"></Input>
                        <i-switch size="large" v-model="textType" style="right: 3px;position: absolute;bottom: 3px;">
                          <span slot="open" >Json</span>
                          <span slot="close">Text</span>
                        </i-switch>
                      </Card>
                    </Col>
                  </Row>
                  <div>
                    <Input style="margin-top: 4px;"
                           v-if="!textType"
                           v-model="currentSelectRowData.value"
                           type="textarea"
                           :autosize="{minRows: 6,maxRows: 30}" placeholder="列值"></Input>
                    <Button
                      v-if="!textType"
                      style="float: right"
                      @click="updateValue(key, data, 'updateRowValue')"
                      :loading="buttonLoading"
                    >保存</Button>

                    <vue-json-pretty
                      v-if="textType"
                      :path="'res'"
                      :data="formatJson(currentSelectRowData.value, key)"
                    >
                    </vue-json-pretty>
                  </div>
                </div>
              </TabPane>
            </Tabs>
            </div>
            <div v-else style="text-align: center;">
              <img draggable="false" src="static/rdm_logo.png" style="width: 20%; margin-top: 100px;"/>
              <p style="font-size: 16px; font-weight: bold;  margin-top:100px;color: #000;">RedisManager - Redis客户端管理工具 @ By Xiusin</p>
            </div>

            <div v-if="currentConnectionId != '' && pubsubModal" style="position:absolute; z-index: 10;  top: 64px;background: #fff;width: 100%;height: 100%; padding:10px;">
                <ul class="infinite-list" style="position:relative; top: 30px;">
                  <li class="infinite-list-item"  v-for="(item, index) in chanMegs[getPubSubTabKey()]">{{item}}</li>
                </ul>

                 <div style="position:absolute; top:8px; left:20px; width:100%">
                   <Row>
                  <Col span="6" >
                    <Input :name="currentConnection + 'addinput'" v-model="customChannel" placeholder="如果填写则选项失效">
                            <span slot="prepend">自定义频道</span>
                    </Input>
                  </Col>
                  <Col span="10" offset="1">
                    <Input :name="currentConnection + 'input'" @keyup.enter.native="sendToChannel"  v-model="channelMsg" placeholder="发布内容到订阅的频道">
                            <span slot="prepend"><Select v-model="selectedChannel" style="width:120px" placeholder="选择频道">
                              <Option v-for="(item, index) in channels" :key="'channel_' + item"
                                      :label="item"
                                      :value="item">
                              </Option>
                            </Select></span>
                          </Input>
                  </Col>
                </Row>
              </div>
            </div>

            <div v-if="currentConnectionId != '' && infoModal" style="position:absolute; z-index: 10;  top: 64px;background: #fff;width: 100%;height: 100%; padding:10px;">
              <Tabs value="first" :animated="false" style="height: 100%">
                <TabPane label="慢日志" name="first" style="height: 100%">
                  <Table size="small" :columns="slowLogColumns" :data="slowLogs" :stripe="true" :border="true" style="height: 100%"></Table>
                </TabPane>
                <TabPane label="配置信息" name="second" style="height: 100%"><Table size="small" :columns="serverConfigColumns" :data="serverConfig" :stripe="true" :border="true" style="height: 100%"></Table></TabPane>
                <TabPane label="服务器信息" name="three" style="height: 100%">
                  <Collapse v-model="infoCollapse">
                    <Panel v-for="(val11, key11) in serverInfo" :key="key11">
                      {{key11}}
                      <pre slot="content">{{val11}}</pre>
                    </Panel>
                  </Collapse>
                </TabPane>
              </Tabs>
            </div>
          </Content>
        </Layout>
      </Layout>
    </Layout>

    <Modal v-model="connectionModal" width="360">
      <p slot="header" style="color:#f60;">
        <Icon type="ios-information-circle"></Icon>
        <span>配置Redis连接信息</span>
      </p>
      <div>
        <Form :model="formItem" :label-width="80">
          <FormItem label="名称:">
            <Input v-model="formItem.title" placeholder="连接名称"></Input>
          </FormItem>

          <FormItem label="地址:">
            <Input v-model="formItem.ip" placeholder="请输入IP地址"></Input>
          </FormItem>

          <FormItem label="端口:">
            <Input v-model="formItem.port" placeholder="请输入IP端口"></Input>
          </FormItem>

          <FormItem label="密码:">
            <Input v-model="formItem.auth" placeholder="授权密码(可选)"></Input>
          </FormItem>

        </Form>
      </div>
      <div slot="footer">
        <Row :gutter="24">
          <Col span="8" style="text-align: left">
            <Button type="info" size="small" :loading="modal_loading" @click="connectionTestHandler()">测试连接</Button>
          </Col>
          <Col span="16">
            <Button type="primary" size="small" :loading="modal_loading" @click="connectionSaveHandler()">确定</Button>
            <Button type="error" size="small" @click="connectionModal=false">取消</Button>
          </Col>
        </Row>
      </div>
    </Modal>

    <Modal v-model="ttlModal" width="360">
      <p slot="header" style="color:#f60;">
        <Icon type="ios-information-circle"></Icon>
        <span>设置 {{ttlValue.key}} TTL</span>
      </p>
      <div>
        <Form :label-width="80">
          <FormItem label="名称:">
            <Input v-model="ttlValue.data.ttl" placeholder="设置TTL时间"></Input>
          </FormItem>
        </Form>
      </div>
      <div slot="footer">
        <Row :gutter="24">
          <Col span="24">
            <Button type="primary" style="float: right" size="small" :loading="modal_loading" @click="updateValue(ttlValue.key, ttlValue.data, 'ttl')">确定</Button>
          </Col>
        </Row>
      </div>
    </Modal>

    <Modal v-model="addKeyModal" width="500">
      <p slot="header" style="color:#f60;">
        <Icon type="ios-information-circle"></Icon>
        <span>添加新键</span>
      </p>
      <div>
        <Form :label-width="50">
          <FormItem label="键名:">
            <Input v-model="newValue.key" placeholder=""></Input>
          </FormItem>
          <FormItem label="类型:">
            <RadioGroup v-model="newKeyType" type="button">
              <Radio label="string"></Radio>
              <Radio label="list"></Radio>
              <Radio label="set"></Radio>
              <Radio label="zset"></Radio>
              <Radio label="hash"></Radio>
            </RadioGroup>
          </FormItem>
          <FormItem label="分值:" v-if="newKeyType === 'zset'">
            <Input v-model="newValue.keyorscore" placeholder=""></Input>
          </FormItem>
          <FormItem label="键:" v-if="newKeyType === 'hash'">
            <Input v-model="newValue.keyorscore" placeholder=""></Input>
          </FormItem>
          <FormItem label="值:">
            <Input v-model="newValue.data" type="textarea" :autosize="{minRows: 5,maxRows: 5}" ></Input>
          </FormItem>
        </Form>
      </div>
      <div slot="footer">
        <Row :gutter="24">
          <Col span="24">
            <Button
              type="primary"
              style="float: right"
              size="small"
              :loading="buttonLoading"
              @click="addNewKey"
            >确定</Button>
          </Col>
        </Row>
      </div>
    </Modal>

    <Modal v-model="addRowModal" width="360">
      <p slot="header" style="color:#f60;">
        <Icon type="ios-information-circle"></Icon>
        <span>{{rowValue.key}} 添加行操作</span>
      </p>
      <div>
        <Input v-model="rowValue.newRowKey" style="margin-bottom: 5px" v-if="rowValue.data.type === 'hash'" placeholder="请输入新key"></Input>
        <Input v-model="rowValue.newRowKey" style="margin-bottom: 5px" v-if="rowValue.data.type === 'zset'" placeholder="请输入分值"></Input>
        <Input v-model="rowValue.newRowValue" type="textarea" placeholder="请输入数据"></Input>
      </div>
      <div slot="footer">
        <Row :gutter="24">
          <Col span="24">
            <Button type="primary" style="float: right" size="small" :loading="modal_loading" @click="updateValue(rowValue.key, rowValue, 'addrow')">确定</Button>
          </Col>
        </Row>
      </div>
    </Modal>

    <Modal
      v-model="confirmModal"
      title="操作提醒"
      @on-ok="confirmModalEvent"
    >
      <p>{{confirmModalText}}</p>
      <div slot="footer">
        <Row :gutter="24">
          <Col>
            <Button type="error" size="small" @click="confirmModal=false">取消</Button>
            <Button type="primary" size="small" @click="confirmModalEvent">确定</Button>
          </Col>
        </Row>
      </div>
    </Modal>

    <Modal v-model="showJsonModal" fullscreen footer-hide :title="currentConnection" :on-visible-change="showJsonModalOkClick">
        <VueTerminal v-bind:id="currentConnectionId"
                     v-bind:index="currentDbIndex"
                     @command="onCliCommand"
                     console-sign="redis-cli $"  style="height: 100%; font-size:14px"></VueTerminal>
    </Modal>

  </div>
</template>
<script>
  import Vue from 'vue'
  import VueJsonPretty from 'vue-json-pretty'
  import VueTerminal from '../../vue-terminal-ui'
  import Api from '../api'
  import $ from 'jquery'
  export default {
    name: 'MainPage',
    components: {
      VueJsonPretty,
      VueTerminal
    },
    data () {
      return {
        infoCollapse: '',
        slowLogColumns: [
          {
            title: '时间',
            key: 'time',
            width: 170 // 不加这东西
          },
          {
            title: '耗时(μs)',
            key: 'used_time',
            width: 120,
            sortable: true
          },
          {
            title: '命令',
            key: 'command'
          }
        ],
        slowLogs: [],
        serverConfig: [],
        serverConfigColumns: [
          {
            title: '配置项',
            key: 'key',
            width: 200 // 不加这东西
          },
          {
            title: '值',
            key: 'value'
          }
        ],
        serverInfo: {},
        customChannel: '',
        chanMegs: {}, // 消息内容
        channelMsg: '',
        selectedChannel: '',
        pubsubModal: false,
        showJsonModal: false,
        terminalTabs: {},
        cliOpen: false,
        channels: [],
        inited: false,
        newKeyType: 'string',
        searchKey: '',
        textType: false,
        currentConnectionId: 0,
        buttonLoading: false,
        currentKey: '',
        currentConnection: '',
        currentDbIndex: -1,
        currentSelectRowData: {}, // 用于行列选择
        currentHandleNodeData: {}, // 用于基于当前操作数据的节点
        formItem: {
          title: '',
          ip: '127.0.0.1',
          port: '6379',
          auth: ''
        },
        ttlValue: {
          'data': {},
          'key': ''
        },
        rowValue: {
          'data': {},
          'key': '',
          'score': 100,
          'newRowKey': '',
          'newRowValue': ''
        },
        newValue: {
          'data': '',
          'key': '',
          'keyorscore': '',
          'db': -1,
          'redis_id': 0
        },
        infoModal: false,
        connectionListData: [],
        connectionTreeList: [],
        tabs: {},
        connectionModal: false,
        ttlModal: false,
        addKeyModal: false,
        addRowModal: false,
        modal_loading: false,
        buttonProps: {
          // type: 'ghost',
          size: 'small'
        },
        confirmModal: false,
        confirmModalText: '',
        confirmModalEvent: () => {}
      }
    },
    created () {
      let commandOrControlKeyDown = false
      document.onkeydown = (e) => {
        let key = window.event.keyCode
        if (key === 93) {
          commandOrControlKeyDown = true
        }
        // 屏蔽command+r f12的刷新行为
        if ((key === 82 && commandOrControlKeyDown) || key === 123) {
          e.preventDefault()
          return false
        }
        return true
      }
      document.onkeyup = (e) => {
        let key = window.event.keyCode
        if (key === 93) {
          commandOrControlKeyDown = false
        }
      }
    },
    mounted () {
      if (!window.require) {
        this.initWs(() => {
          this.getConnectionList()
          this.channelWs()
        })
      } else {
        this.initWs()
        window.setTimeout(() => {
          this.getConnectionList()
          this.channelWs()
        }, 300)
      }
    },
    methods: {
      channelWs () {
        let that = this
        if (!window.require) {
          window.$websocket.onmessage = (event) => {
            let data = JSON.parse(event.data)
            let message = data
            console.log('onmessage', message)
            let channel = this.customChannel !== '' ? this.customChannel : this.selectedChannel
            this.channelMsg = ''
            this.loadPubSubChannels()
            this.$Message.success('发送到channel:' + channel + '的消息成功')
            if (!that.chanMegs.hasOwnProperty(message.id + '')) {
              that.chanMegs[message.id + ''] = []
            }
            that.chanMegs[message.id + ''].unshift('[ ' + message.time + ' ]  收到频道(' + message.channel + ') 的消息:  ' + message.data)
            that.chanMegs = Object.assign({}, that.chanMegs)
          }
        } else {
          window.astilectron.onMessage((message) => {
            if (!that.chanMegs.hasOwnProperty(message.id + '')) {
              that.chanMegs[message.id + ''] = []
            }
            that.chanMegs[message.id + ''].unshift('[ ' + message.time + ' ]  收到频道(' + message.channel + ') 的消息:  ' + message.data)
            that.chanMegs = Object.assign({}, that.chanMegs)
          })
        }
      },
      getPubSubTabKey () {
        return this.currentConnectionId + ''
      },
      sendToChannel () {
        if (this.selectedChannel === '' && this.customChannel === '') {
          this.$Message.error('请选择channel或输入自定义频道')
        } else if (this.channelMsg !== '') {
          let channel = this.customChannel !== '' ? this.customChannel : this.selectedChannel
          if (!window.require) {
            window.$websocket.send(JSON.stringify({
              id: this.currentConnectionId,
              channel: channel,
              msg: this.channelMsg
            }))
          } else {
            Api.pubSub({
              id: this.currentConnectionId,
              channel: channel,
              msg: this.channelMsg
            }, (data) => {
              if (data.status === 200) {
                this.channelMsg = ''
                this.loadPubSubChannels()
                this.$Message.success('发送到channel:' + channel + '的消息成功')
              } else {
                this.$Message.error(data.msg)
              }
            })
          }
        }
      },
      openPubSubTab () {
        if (this.pubsubModal) {
          this.pubsubModal = false
          return
        }
        this.pubsubModal = true
        this.infoModal = false
        this.loadPubSubChannels()
      },
      openInfoTab () {
        if (this.infoModal) {
          this.infoModal = false
          return
        }
        this.infoModal = true
        this.pubsubModal = false
        this.loadInfo()
      },
      loadInfo () {
        Api.info({
          id: this.currentConnectionId
        }, (data) => {
          if (data.status === 200) {
            let dataStrs = data.data.data.split('\n')
            let infos = []
            let objVal = []
            let objKey = ''
            this.infoCollapse = ''
            for (let i = 0; i < dataStrs.length; i++) {
              if (dataStrs[i].indexOf('# ') > -1) {
                if (objVal.length > 0 && objKey !== '') {
                  infos[objKey] = objVal.join('\n')
                  if (this.infoCollapse === '') {
                    this.infoCollapse = objKey
                  }
                }
                objKey = dataStrs[i]
                objVal = []
              } else {
                objVal.push(dataStrs[i])
              }
            }
            this.serverInfo = Object.assign({}, infos)
            let config = []
            let srvConfig = data.data.config
            for (let i = 0; i < srvConfig.length; i = i + 2) {
              config.push({
                'key': srvConfig[i],
                'value': srvConfig[i + 1]
              })
            }
            this.serverConfig = config // config
            this.slowLogs = data.data.slowLogs
            console.log(this.slowLogs, data.data)
          } else {
            this.$Message.error(data.msg)
          }
        })
      },
      loadPubSubChannels () {
        Api.pubSub({
          id: this.currentConnectionId
        }, (data) => {
          if (data.status === 200) {
            this.channels = data.data
          } else {
            this.$Message.error(data.msg)
          }
        })
      },
      showJsonModalOkClick () {
        this.showJsonModal = false
      },
      isEmptyObj (obj) {
        return JSON.stringify(obj) === '{}'
      },
      addNewKey () {
        this.modal_loading = true
        const param = {
          key: this.newValue.key,
          data: this.newValue.data,
          type: this.newKeyType,
          rowKey: this.newKeyType === 'zset' ? Number(this.newValue.keyorscore) : this.newValue.keyorscore,
          id: this.newValue.redis_id,
          index: this.newValue.db
        }
        Api.addKey(param, (res) => {
          this.currentConnectionId = this.newValue.redis_id
          this.currentDbIndex = this.newValue.db
          this.modal_loading = false
          if (typeof this.tabs[this.getTabsKey()] === undefined) {
            this.tabs[this.getTabsKey()] = {keys: {}}
            this.tabs = Object.assign({}, this.tabs) // 绑定为动态变量,否则页面不会动态渲染
          }
          if (res.status !== 200) {
            this.$Message.error(res.msg)
          } else {
            this.$Message.success(res.msg)
            this.addKeyModal = false
            // todo 刷新Tree的节点
            if (this.currentHandleNodeData.data.expand) {
              this.append(this.currentHandleNodeData.data, param, 'addkey')  // 只有父节点展开的时候才添加节点
            }
            this.currentHandleNodeData = {}
            this.updateDbKeyCount('add')
          }
        })
      },
      formatJson (data, key) {
        if (this.currentKey === key) {
          try {
            return JSON.parse(data)
          } catch (e) {
            this.$Message.error('内容无法解析为JSON, 按钮切回Text')
            this.textType = false
          }
        }
      },
      getRowData (data, index) {
        this.currentSelectRowData = {
          value: data.value,
          key: data.key,
          oldValue: data.value,
          index: index
        }
      },
      removeRow (key, data) {
        this.buttonLoading = true
        Api.removeRow({
          key: key,
          data: data.type === 'hash' ? this.currentSelectRowData.key : this.currentSelectRowData.value,
          type: data.type,
          id: this.currentConnectionId,
          index: this.currentDbIndex
        }, (res) => {
          this.buttonLoading = false
          if (res.status !== 200) {
            this.$Message.error(res.msg)
          }
          let tmp = []
          let isremove = false
          if (data.type !== 'hash' && data.type !== 'zset') {
            for (let i = 0; i < data.data.length; i++) {
              if (!isremove && data.data[i] === this.currentSelectRowData.value) {
                isremove = true
                continue
              }
              tmp.push(data.data[i])
            }
            isremove = false
            data.data = tmp
          } if (data.type === 'zset') {
            for (let i = 0; i < data.data.length; i++) {
              if (!isremove && data.data[i].score === this.currentSelectRowData.key) {
                isremove = true
                continue
              }
              tmp.push(data.data[i])
            }
            data.data = tmp
            isremove = false
          } else {
            delete data.data[this.currentSelectRowData.key]
          }
          this.currentSelectRowData = {}
        })
      },
      addRow (key, data) {
        this.addRowModal = true
        this.rowValue.key = key
        this.rowValue.data = data
        this.rowValue.newRowValue = ''
        this.rowValue.newRowKey = ''
      },
      setTTL (key, data) {
        this.ttlModal = true
        this.ttlValue.key = key
        this.ttlValue.data = data
      },
      updateValue (key, data, action) {
        // 判断操作
        let type = data.type
        let rowIndex = null
        let newRowValue = data.newRowValue
        let newRowKey = data.newRowKey
        if (action === 'addrow') {
          type = data.data.type
          rowIndex = data.data.data.length
          let rowKey = type === 'zset' ? rowIndex : (newRowKey || rowIndex)
          this.$set(data.data, rowKey, data.newRowValue)
          if (typeof data.data.data === 'object' && (data.newRowKey) && type !== 'zset') {
            data.data.data[data.newRowKey] = data.newRowValue
          } else if (type === 'zset') {
            rowIndex = Number(data.newRowKey)
          }
        } else if (action === 'updateRowValue') {
          rowIndex = this.currentSelectRowData.index
          newRowKey = this.currentSelectRowData.key
          newRowValue = this.currentSelectRowData.value
          this.$set(data.data, newRowKey || rowIndex, this.currentSelectRowData.value)
          if (type === 'set' || type === 'zset') {
            rowIndex = this.currentSelectRowData.oldValue
          }
        }
        this.buttonLoading = true
        Api.updateKey({
          key: key,
          data: type !== 'string' ? newRowValue : data.data,
          type: type,
          ttl: Number(data.ttl),
          action: action !== 'ttl' ? action : 'ttl',
          rowkey: type === 'hash' ? newRowKey : rowIndex,
          id: this.currentConnectionId,
          index: this.currentDbIndex
        }, (res) => {
          this.buttonLoading = false
          if (res.status !== 200) {
            this.$Message.error(res.msg)
          } else {  // 设置成功
            this.addRowModal = false
            this.ttlModal = false
            this.$Message.success(res.msg)
            if (action === 'addrow') {
              data.data.data.push(type === 'zset' ? {
                'score': type === 'hash' ? newRowKey : rowIndex,
                'value': data.newRowValue
              } : data.newRowValue)
            }
          }
        })
      },
      removeKey (key, callback) {
        this.buttonLoading = true
        Api.removeKey({
          key: key,
          id: this.currentConnectionId,
          index: this.currentDbIndex
        }, (res) => {
          console.log('删除返回结果:', res, {
            key: key,
            id: this.currentConnectionId,
            index: this.currentDbIndex
          })
          this.buttonLoading = false
          if (res.status !== 200) {
            this.$Message.error(res.msg)
            return
          }
          this.handleTabRemove(key)
          if (callback) { // 这种情况是在treenode里删除的
            callback()
          } else {  // 这是直接使用tab里的remove
            for (let i in this.connectionTreeList) {
              if (this.currentConnectionId === this.connectionTreeList[i].data.id) {
                const children = this.connectionTreeList[i].children[this.currentDbIndex].children
                for (let j in children) {
                  if (children[j].title === key) {
                    this.connectionTreeList[i].children[this.currentDbIndex].children.splice(j, 1)
                    this.updateDbKeyCount('sub')
                    return
                  }
                }
              }
            }
          }
        })
      },
      flushKey (key) {
        this.buttonLoading = true
        Api.connectionServer({
          id: this.currentConnectionId,  // 连接数
          index: this.currentDbIndex,
          action: 'get_value',
          key: key
        }, (res) => {
          if (res.status === 5000) {
            this.$Message.error(res.msg)
            return
          } else if (res.status === 5001) {
            let prv = ''
            delete this.tabs[this.getTabsKey()].keys[key]
            for (let i in this.tabs[this.getTabsKey()].keys) {
              prv = i
            }
            this.currentKey = prv
            this.tabs = Object.assign({}, this.tabs) // 绑定为动态变量,否则页面不会动态渲染
            return
          }
          this.tabs[this.getTabsKey()].keys[key] = res.data
          this.tabs = Object.assign({}, this.tabs) // 绑定为动态变量,否则页面不会动态渲染
          this.currentKey = key
        })
      },
      handleTabRemove (key) {
        let prv = ''
        for (let i in this.tabs[this.getTabsKey()].keys) {
          if (i === key) {
            break
          }
          prv = i
        }
        delete this.tabs[this.getTabsKey()].keys[key]
        this.currentKey = prv
      },
      getTabsKey () {
        return this.currentConnectionId + '-' + this.currentDbIndex
      },
      selectChange (nodes) {
        if (nodes.length === 0) return
        let node = nodes[0]
        if (node.action !== 'get_value') return
        this.pubsubModal = false
        this.infoModal = false
        let key = (node.group ? node.group + ':' : '') + node.title
        if (typeof this.tabs[this.getTabsKey()] === undefined) {
          this.tabs[this.getTabsKey()] = {keys: {}}
        }
        if (!Object.keys(this.tabs[this.getTabsKey()].keys).includes(key)) {
          Api.connectionServer({
            id: node.redis_id,  // 连接数
            index: node.index,
            action: node.action,
            key: key
          }, (res) => {
            console.log(res)
            if (res.status === 5000) {
              this.$Message.error(res.msg)
              return
            } else if (res.status === 5001) {
              delete this.tabs[this.getTabsKey()].keys[key]
              this.tabs = Object.assign({}, this.tabs) // 绑定为动态变量,否则页面不会动态渲染
              return
            }
            this.currentDbIndex = node.index
            this.currentConnectionId = node.redis_id
            this.tabs[this.getTabsKey()].keys[key] = res.data
            this.tabs = Object.assign({}, this.tabs) // 绑定为动态变量,否则页面不会动态渲染
            this.currentKey = key
          })
        } else {
          this.currentKey = key
        }
      },
      formatItem (type, data) {
        let res = []
        switch (type) {
          case 'hash':
            for (let i in data) {
              if ((this.searchKey && (i.indexOf(this.searchKey) > -1 || data[i].indexOf(this.searchKey) > -1)) || !this.searchKey) {
                res.push({
                  key: i,
                  value: data[i]
                })
              }
            }
            break
          case 'zset':
            for (let i in data) {
              if ((this.searchKey && (data[i]['score'].indexOf(this.searchKey) > -1 || data[i]['value'].indexOf(this.searchKey) > -1)) || !this.searchKey) {
                res.push({
                  key: data[i]['score'],
                  value: data[i]['value']
                })
              }
            }
            break
          default:
            for (let i = 0; i < data.length; i++) {
              if (!this.searchKey || (this.searchKey && data[i].indexOf(this.searchKey) > -1)) {
                res.push({
                  value: data[i]
                })
              }
            }
        }
        return res
      },
      getColumns (type) {
        let cols = []
        switch (type) {
          case 'hash':
            cols = [
              {
                type: 'index',
                width: 60,
                align: 'center'
              },
              {
                title: '键',
                width: 160,
                key: 'key'
              },
              {
                title: '值',
                key: 'value'
              }
            ]
            break
          case 'zset':
            cols = [
              {
                type: 'index',
                width: 60,
                align: 'center'
              },
              {
                title: '值',
                width: 160,
                key: 'value'
              },
              {
                title: 'SCORE',
                key: 'key',
                sortable: true,
                sortType: 'asc'
              }
            ]
            break
          default:
            cols = [
              {
                type: 'index',
                width: 60,
                align: 'center'
              },
              {
                title: '值',
                key: 'value'
              }
            ]
        }
        return cols
      },
      showLoginModal () {
        this.modal_loading = false
        this.connectionModal = true
      },
      connectionTestHandler () {
        this.modal_loading = true
        Api.connectionTest(this.formItem, (res) => {
          this.modal_loading = false
          if (res.status !== 200) {
            this.$Message.error(res.msg)
          } else {
            this.$Message.success(res.msg)
          }
        })
      },
      onCliCommand (data, resolve, reject) {
        setTimeout(() => {
          resolve('')
        }, 300)
      },
      initWs (callback) {
        if (callback) {
          window.astilectron = {}
          window.$websocket = new WebSocket('ws://localhost:18998/redis/connection/pubsub')
          window.astilectron.post = (url, data, c) => {
            console.log('post', data)
            $.post('http://localhost:18998' + url, data, (message) => {
              this.buttonLoading = false
              console.log('message:', message)
              try {
                c(JSON.parse(message))
              } catch (e) {
                c(message)
              }
            })
          }
          window.astilectron.get = (url, data, c) => {
            console.log('post', data)
            $.getJSON('http://localhost:18998' + url, data, (message) => {
              this.buttonLoading = false
              if (typeof message === 'string') {
                return c(JSON.parse(message))
              } else {
                return c(message)
              }
            })
          }
          Vue.prototype.$Websocket = window.astilectron
          if (callback) callback()
        } else {
          window.document.addEventListener('astilectron-ready', () => {
            if (window.astilectron.post === undefined) {
              window.astilectron.post = (url, data, c) => {
                console.log('post:' + url, data)
                window.astilectron.sendMessage(url + (data ? '___::___' + JSON.stringify(data) : ''), (message) => {
                  this.buttonLoading = false
                  console.log('message:', message)
                  try {
                    c(JSON.parse(message))
                  } catch (e) {
                    c(message)
                  }
                })
              }
              window.astilectron.get = (url, data, c) => {
                window.astilectron.sendMessage(url + (data ? '___::___' + JSON.stringify(data) : ''), (message) => {
                  this.buttonLoading = false
                  if (typeof message === 'string') {
                    return c(JSON.parse(message))
                  } else {
                    return c(message)
                  }
                })
              }
            }
            Vue.prototype.$Websocket = window.astilectron
          })
        }
      },
      getConnectionList () {
        this.connectionTreeList = []
        this.connectionListData = []
        Api.connectionList((res) => {
          this.modal_loading = false
          if (res.status !== 200) {
            this.$Message.error(res.msg)
          } else {
            this.connectionListData = res.data
            for (let i = 0; i < res.data.length; i++) {
              this.connectionTreeList.push({
                title: res.data[i].title,
                expand: false,
                loading: false,
                action: 'dblist',
                data: res.data[i],
                render: this.connectionRenderFunc,
                children: []
              })
            }
          }
        })
      },
      connectionRenderFunc (h, { root, node, data }) {
        return h('span', {
          style: {
            display: 'inline-block',
            width: '100%'
          }
        }, [
          h('span', [
            h('Icon', {
              props: {
                type: 'social-buffer'
              },
              style: {
                marginRight: '5px'
              }
            }), // 图标
            h('span', data.title) // 标题
          ]),
          h('span', { // 右边菜单位置
            style: {
              display: 'inline-block',
              float: 'right',
              marginRight: '32px'
            }
          }, [
            h('Button', {
              attrs: {
                title: '断开连接'
              },
              style: {
                marginRight: '3px'
              },
              props: Object.assign({}, this.buttonProps, {
                icon: 'ios-close'
              }),
              on: {
                click: () => {
                  if (data.children.length === 0) {
                    this.$Message.error('连接未开启或已关闭')
                    return
                  }
                  this.confirmModalText = '是否要断开数据库连接?'
                  this.confirmModal = true
                  this.confirmModalEvent = () => {
                    this.confirmModal = false
                    data.expand = false
                    this.currentConnectionId = 0
                    this.currentConnection = ''
                    this.clearAll(data)
                  }
                }
              }
            }),
            h('Button', {
              attrs: {
                title: '删除链接'
              },
              style: {
                marginRight: '3px'
              },
              props: Object.assign({}, this.buttonProps, {
                icon: 'ios-trash-outline'
              }),
              on: {
                click: () => {
                  this.confirmModalText = '是否要删除链接信息?'
                  this.confirmModal = true
                  this.confirmModalEvent = () => {
                    Api.removeConnection({
                      id: data.data.id
                    }, (res) => {
                      this.confirmModal = false
                      if (res.status !== 200) {
                        this.$Message.error(res.msg)
                        return
                      }
                      let getIndex = -1
                      for (let i in this.connectionTreeList) {
                        if (this.connectionTreeList[i].nodeKey === node.nodeKey) {
                          getIndex = i
                          break
                        }
                      }
                      if (getIndex > -1) {
                        this.currentConnectionId = 0
                        this.currentConnection = ''
                        this.connectionTreeList.splice(getIndex, 1)
                      }
                    })
                  }
                }
              }
            })
          ])
        ])
      },
      connectionServer () {
        this.modal_loading = true
        Api.connectionServer(this.formatItem, (res) => {
          this.modal_loading = false
          if (res.status !== 200) {
            this.$Message.error(res.msg)
          } else {
            this.$Message.success(res.msg)
          }
        })
      },
      connectionList () {
        this.modal_loading = true
        Api.connectionList((res) => {
          if (res.status !== 200) {
            this.$Message.error(res.msg)
          } else {
            this.$Message.success(res.msg)
          }
        })
      },
      connectionSaveHandler () {
        this.modal_loading = true
        Api.connectionSave(this.formItem, (res) => {
          this.modal_loading = false
          if (res.status !== 200) {
            this.$Message.error(res.msg)
          } else {
            // 添加数据到内容
            this.formItem = {
              title: '',
              ip: '',
              port: '6379',
              auth: ''
            }
            this.$Message.success(res.msg)
            this.connectionModal = false
            this.connectionTreeList.push({
              title: res.data.title,
              expand: false,
              loading: false,
              action: 'dblist',
              data: res.data,
              render: this.connectionRenderFunc,
              children: []
            })
          }
        })
      },
      remove (root, node, data) {
        console.log('remove', root, node, data)
        const parentKey = root.find(el => el === node).parent
        const parent = root.find(el => el.nodeKey === parentKey).node
        const index = parent.children.indexOf(data)
        parent.children.splice(index, 1)
      },
      append (data, param, action) {
        if (action === 'addkey') {
          const children = data.children || []
          children.push({
            title: param.key,
            redis_id: param.id,
            action: 'get_value',
            selected: false,
            index: param.index,
            render: this.keyRenderFunc
          })
          this.$set(data, 'children', children)
        }
      },
      clearAll (data) {
        this.$set(data, 'children', [])
      },
      updateDbKeyCount (action) {
        for (let i in this.connectionTreeList) {
          if (this.currentConnectionId === this.connectionTreeList[i].data.id) {
            let node = this.connectionTreeList[i].children[this.currentDbIndex]
            console.log(node)
            if (node) {
              node.count = action === 'add' ? node.count + 1 : node.count - 1
              node.title = 'DB' + this.currentDbIndex + ' (' + node.count + ')'
            }
            break
          }
        }
      },
      loadData (item, callback) {
        switch (item.action) {
          case 'dblist':
            Api.connectionServer({
              id: item.data.id,
              action: item.action
            }, (res) => {
              if (res.status !== 200) {
                this.$Message.error(res.msg)
              } else {
                this.currentConnectionId = item.data.id
                this.currentConnection = item.data.title
                let data = []
                for (let i = 0; i < res.data.length; i++) {
                  data.push({
                    title: 'DB' + i + ' (' + res.data[i] + ')',
                    loading: false,
                    db: i,  // dbindex
                    count: res.data[i],
                    selected: false,
                    redis_id: item.data.id, // 继续redis_id
                    action: 'select_db',
                    render: (h, { root, node, data }) => {
                      return h('span', {
                        style: {
                          display: 'inline-block',
                          width: '100%'
                        }
                      }, [
                        h('span', [
                          h('Icon', {
                            props: {
                              type: 'social-buffer'
                            },
                            style: {
                              marginRight: '5px'
                            }
                          }), // 图标
                          h('span', data.title) // 标题
                        ]),
                        h('span', { // 右边菜单位置
                          style: {
                            display: 'inline-block',
                            float: 'right',
                            marginRight: '32px'
                          }
                        }, [
                          h('Button', {
                            attrs: {
                              title: '刷新'
                            },
                            style: {
                              marginRight: '3px'
                            },
                            props: Object.assign({}, this.buttonProps, {
                              icon: 'ios-sync'
                            }),
                            on: {
                              click: () => {
                                let item = node.node
                                Api.connectionServer({
                                  id: item.redis_id,
                                  index: item.db,
                                  action: item.action
                                }, (res) => {
                                  if (res.status !== 200) {
                                    this.$Message.error(res.msg)
                                    return
                                  }
                                  if (!this.tabs.hasOwnProperty(this.getTabsKey())) {
                                    this.tabs[this.getTabsKey()] = {
                                      keys: {}
                                    }
                                  }
                                  let data = []
                                  var count = 0
                                  for (let i in res.data) {
                                    let children = []
                                    count++
                                    if (res.data[i].length > 1 || res.data[i][0] !== i) {
                                      for (let y = 0; y < res.data[i].length; y++) {
                                        children.push({
                                          title: res.data[i][y],
                                          group: i,
                                          index: item.db,
                                          selected: false,
                                          redis_id: item.redis_id,
                                          render: this.keyRenderFunc,
                                          action: 'get_value'
                                        })
                                      }
                                    }
                                    let v = {
                                      title: i,
                                      redis_id: item.redis_id,
                                      action: 'get_value',
                                      selected: false,
                                      index: item.db
                                    }
                                    if (children.length > 0) {
                                      v.children = children
                                      v.action = 'group'
                                    } else {
                                      v.render = this.keyRenderFunc
                                    }
                                    data.push(v)
                                  }
                                  console.log('res.data', res.data)
                                  item.children = data
                                  item.count = count
                                  item.title = 'DB' + item.db + ' (' + item.count + ')'
                                })
                              }
                            }
                          }),
                          h('Button', {
                            attrs: {
                              title: '添加新数据'
                            },
                            style: {
                              marginRight: '3px'
                            },
                            props: Object.assign({}, this.buttonProps, {
                              icon: 'ios-add'
                            }),
                            on: {
                              click: () => {
                                this.newValue.db = data.db
                                this.newValue.redis_id = data.redis_id
                                this.newValue.key = ''
                                this.newValue.keyorscore = ''
                                this.newValue.data = ''
                                this.addKeyModal = true
                                this.currentHandleNodeData = {root, node, data}
                              }
                            }
                          }),
                          h('Button', {
                            attrs: {
                              title: '清空数据库'
                            },
                            props: Object.assign({}, this.buttonProps, {
                              icon: 'ios-trash-outline'
                            }),
                            on: {
                              click: () => {
                                this.confirmModalText = '是否要清空"' + this.currentConnection + '::DB(' + data.db + ')"数据库?'
                                this.confirmModal = true
                                this.confirmModalEvent = () => {
                                  Api.flushDB({
                                    id: data.redis_id,
                                    index: data.db
                                  }, (res) => {
                                    this.confirmModal = false
                                    if (res.status !== 200) {
                                      this.$Message.error(res.msg)
                                    } else {
                                      this.clearAll(data)
                                      this.tabs[this.getTabsKey()]['keys'] = {}
                                      // todo DB节点的key统计数 需要方法!!!
                                    }
                                  })
                                }
                              }
                            }
                          })
                        ])
                      ])
                    },
                    children: []
                  })
                }
                callback(data)
              }
            })

            break
          case 'select_db':
            Api.connectionServer({
              id: item.redis_id,
              index: item.db,
              action: item.action
            }, (res) => {
              if (res.status !== 200) {
                this.$Message.error(res.msg)
                return
              }
              this.currentDbIndex = item.db
              this.currentConnectionId = item.redis_id
              if (!this.tabs.hasOwnProperty(this.getTabsKey())) {
                this.tabs[this.getTabsKey()] = {
                  keys: {}
                }
              }
              let data = []
              for (let i in res.data) {
                let children = []
                if (res.data[i].length > 1 || res.data[i][0] !== i) {
                  for (let y = 0; y < res.data[i].length; y++) {
                    children.push({
                      title: res.data[i][y],
                      group: i,
                      index: item.db,
                      redis_id: item.redis_id,
                      render: this.keyRenderFunc,
                      action: 'get_value'
                    })
                  }
                }
                let v = {
                  title: i,
                  redis_id: item.redis_id,
                  action: 'get_value',
                  index: item.db
                }
                if (children.length > 0) {
                  v.children = children
                  v.action = 'group'
                } else {
                  v.render = this.keyRenderFunc
                }
                data.push(v)
              }
              callback(data)
            })
            break
        }
      },
      keyRenderFunc (h, { root, node, data }) {
        return h('span', {
          style: {
            display: 'inline-block',
            width: '100%'
          }
        }, [
          h('span', [
            h('Icon', {
              props: {
                type: 'social-buffer'
              },
              style: {
                marginRight: '5px'
              }
            }), // 图标
            h('span', {
              domProps: {
                innerHTML: data.title
              },
              on: {
                click: () => {
                  let nodes = []
                  nodes.push(data)
                  this.cliOpen = false
                  this.selectChange(nodes)  // 执行定义方法
                }
              }
            }) // 标题
          ]),
          h('span', { // 右边菜单位置
            style: {
              display: 'inline-block',
              float: 'right',
              marginRight: '32px'
            }
          }, [
            h('Button', {
              attrs: {
                title: '删除键'
              },
              props: Object.assign({}, this.buttonProps, {
                icon: 'ios-remove'
              }),
              on: {
                click: () => {
                  this.confirmModalText = '是否要删除"' + data.title + '"吗?'
                  this.confirmModal = true
                  this.confirmModalEvent = async () => {
                    this.removeKey(data.title, () => {
                      this.remove(root, node, data)
                      this.updateDbKeyCount('sub')
                      this.confirmModal = false
                    })
                  }
                }
              }
            })
          ])
        ])
      }
    }
  }
</script>

<style >

  .infinite-list .infinite-list-item {
    display: flex;
    padding: 5px 10px;
    background: #e8f3fe;
    margin: 10px;
    color: #7dbcfc;
  }

  .ivu-btn-icon-only.ivu-btn-small {
    border-radius: 0;
  }
  .ivu-table {
    overflow-y: auto;
    overflow-x: hidden;
  }

  .ivu-tabs-no-animation > .ivu-tabs-content {
    height: 100%;
  }

  .ivu-btn-icon-only.ivu-btn-small {
    padding: 0px 2px 0px;
    font-size: 10px;
  }
</style>

